package ru.testtask.service.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import ru.testtask.dao.XmlDao;
import ru.testtask.model.XmlDocument;
import ru.testtask.model.XmlElement;
import ru.testtask.service.XmlService;
import ru.testtask.util.XmlParseException;
import ru.testtask.util.XmlParser;

import java.util.*;

@Service("xmlService")
public class XmlServiceImpl implements XmlService {

    private static final Log logger = LogFactory.getLog(XmlServiceImpl.class);

    private final XmlDao xmlDao;

    public XmlServiceImpl(@Qualifier("xmlDao") XmlDao xmlDao) {
        this.xmlDao = xmlDao;
    }

    public XmlDocument transformXmlStringToObjectModel(String filename, String xml) throws XmlParseException {
        List<XmlElement> lst = XmlParser.parseXmlString(xml);
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.setName(filename);
        xmlDoc.setContent(lst);
        return xmlDoc;
    }

    public String transformObjectModelToXmlString(XmlDocument xmlDoc) {
        StringBuilder result = new StringBuilder();

        List<XmlElement> elements = xmlDoc.getContent();

        ListIterator<XmlElement> iter = elements.listIterator();

        Queue<ListIterator<XmlElement>> stack = Collections.asLifoQueue(new LinkedList<ListIterator<XmlElement>>());
        stack.add(iter);

        while ((iter = stack.peek()) != null) {

            boolean gotChilds = false;

            XmlElement element;
            while (iter.hasNext()) {
                element = iter.next();
                result.append(element.printOpenTag());

                if (element.getChilds() != null && element.getChilds().size() > 0) {
                    stack.add(element.getChilds().listIterator());
                    gotChilds = true;
                    break;
                } else {
                    result.append(element.printCloseTag());
                }
            }
            if (!gotChilds) {
                iter = stack.remove();
                if (iter.hasPrevious()) {
                    element = iter.previous();
                    if (element != null && element.getParent() != null) {
                        result.append(element.getParent().printCloseTag());
                    }
                }
            }
        }
        return result.toString();
    }

    @Transactional
    public void saveObjectModel(XmlDocument xmlDoc) {
        xmlDao.saveObjectModel(xmlDoc);
    }

    public XmlDocument readObjectModel(int id) {
        XmlDocument xmlDoc =  xmlDao.readObjectModel(id);

        Map<Integer, XmlElement> elementsMap = new HashMap<Integer, XmlElement>(xmlDoc.getContent().size());
        for (XmlElement element : xmlDoc.getContent()) {
            elementsMap.put(element.getId(), element);
        }

        List<XmlElement> newContent = new LinkedList<XmlElement>();
        for (XmlElement element : xmlDoc.getContent()) {
            if (element.getParent() == null) {
                newContent.add(element);
            } else {
                XmlElement parentElement = elementsMap.get(element.getParent().getId());
                if (parentElement == null) {
                    logger.warn("Element refers to parent that does not exist");
                } else {
                    parentElement.addChild(element, false);
                }
            }
        }
        xmlDoc.setContent(newContent);
        return xmlDoc;
    }

    public List<XmlDocument> getDocumentsList() {
        return xmlDao.getDocumentsList();
    }

}
